home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume89
/
comm
/
amigatcp.2
< prev
next >
Wrap
Text File
|
1989-03-18
|
52KB
|
2,346 lines
Path: xanth!ukma!tut.cis.ohio-state.edu!mailrus!ulowell!page
From: page@swan.ulowell.edu (Bob Page)
Newsgroups: comp.sources.amiga
Subject: v89i081: amigatcp - tcp/ip for the amiga, Part02/06
Message-ID: <12329@swan.ulowell.edu>
Date: 17 Mar 89 23:16:44 GMT
Organization: University of Lowell, Computer Science Dept.
Lines: 2335
Approved: page@swan.ulowell.edu
Submitted-by: rminnich@super.org (Ronald G. Minnich)
Posting-number: Volume 89, Issue 81
Archive-name: comm/amigatcp.2
# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# icmp.c
# mbuf.c
# slip.c
# smtpcli.c
# tcpout.c
# tcpuser.c
# telnet.c
# This archive created: Fri Mar 17 17:57:17 1989
cat << \SHAR_EOF > icmp.c
/* Internet Control Message Protocol */
#include "machdep.h"
#include "internet.h"
#include "timer.h"
#include "ip.h"
#include "icmp.h"
#include "mbuf.h"
int (*echo_proc)(); /* Handler for Echo Reply messages */
struct icmp_errors icmp_errors;
struct icmp_stats icmp_stats;
/* Process an incoming ICMP packet */
void
icmp_input(bp,protocol,source,dest,tos,length,rxbroadcast)
struct mbuf *bp; /* Pointer to ICMP message */
char protocol; /* Should always be ICMP_PTCL */
int32 source; /* Sender of ICMP message */
int32 dest; /* Us */
char tos; /* Type of Service */
int16 length; /* Length of ICMP message */
char rxbroadcast;
{
struct icmp *icmph; /* Pointer to ICMP message */
struct ip_header *iph; /* Offending datagram header */
int16 type; /* Type of ICMP message */
int16 ip_len;
if(rxbroadcast){
/* Broadcast ICMP packets are to be IGNORED !! */
icmp_errors.bdcsts++;
free_p(bp);
return;
}
if(cksum(NULLHEADER,bp,length) != 0){
/* Bad ICMP checksum; discard */
icmp_errors.checksum++;
free_p(bp);
return;
}
/* If the message is fragmented, copy to a contiguous mbuf */
if(bp->next != NULLBUF){
struct mbuf *nbp;
nbp = copy_p(bp,length);
free_p(bp);
if(nbp == NULLBUF){
icmp_errors.nospace++;
return;
}
bp = nbp;
}
icmph = (struct icmp *)bp->data;
/* Process the message. Some messages are passed up to the protocol
* module for handling, others are handled here.
*/
type = icmph->type & 0xff;
if(type < ICMP_TYPES)
icmp_stats.input[type]++;
switch(type){
case TIME_EXCEED: /* Time-to-live Exceeded */
case DEST_UNREACH: /* Destination Unreachable */
case QUENCH: /* Source Quench */
iph = (struct ip_header *)(icmph + 1);
ip_len = (iph->v_ihl & 0xf) * sizeof(int32);
switch(iph->protocol){
case TCP_PTCL:
tcp_icmp(ntohl(iph->source),ntohl(iph->dest),
icmph->type,icmph->code,(char *)iph + ip_len);
break;
}
break;
case ECHO: /* Echo Request */
/* Change type to ECHO_REPLY, recompute checksum,
* and return datagram.
*/
icmph->type = ECHO_REPLY;
icmph->checksum = 0;
icmph->checksum = cksum(NULLHEADER,bp,length);
icmp_stats.output[ECHO_REPLY]++;
ip_send(dest,source,ICMP_PTCL,tos,0,bp,length,0,0);
return;
case REDIRECT: /* Redirect */
case PARAM_PROB: /* Parameter Problem */
break;
case ECHO_REPLY: /* Echo Reply */
if(echo_proc){
(*echo_proc)(ntohl(iph->source),ntohl(iph->dest),
icmph->type,icmph->code,(char *)iph + ip_len);
}
break;
case TIMESTAMP: /* Timestamp */
case TIME_REPLY: /* Timestamp Reply */
case INFO_RQST: /* Information Request */
case INFO_REPLY: /* Information Reply */
break;
}
free_p(bp);
}
/* Return an ICMP response to the sender of a datagram */
icmp_output(bp,type,code,args)
struct mbuf *bp; /* Pointer to offending IP header + data */
char type,code; /* Codes to send */
union icmp_args *args;
{
struct ip_header *iph; /* Offending IP header */
int16 ip_len; /* Length of offending IP header */
struct mbuf *reply; /* Buffer with ICMP reply */
struct icmp *icmph; /* ICMP protocol header */
struct mbuf *data; /* Returned portion of offending packet */
int16 dlen; /* Length of data portion of offending pkt */
int16 length; /* Total length of reply */
extern int32 ip_addr; /* Our IP address */
if(type < ICMP_TYPES)
icmp_stats.output[type]++;
iph = (struct ip_header *)bp->data;
if(iph->protocol == ICMP_PTCL){
icmp_errors.noloop++;
return; /* Never send an ICMP message about another ICMP message */
}
/* Compute amount of original datagram to return.
* We return the original IP header, and up to 8 bytes past that.
*/
ip_len = (iph->v_ihl & 0xf) * sizeof(int32);
dlen = ntohs(iph->length);
if(dlen > ip_len + 8)
dlen = ip_len + 8;
length = sizeof(struct icmp) + dlen;
/* Allocate ICMP header and fill in */
if((reply = alloc_mbuf(sizeof(struct icmp))) == NULLBUF){
/* No space; don't bother */
icmp_errors.nospace++;
return;
}
reply->cnt = sizeof(struct icmp);
icmph = (struct icmp *)reply->data;
icmph->type = type;
icmph->code = code;
if(args != (union icmp_args *)NULL)
icmph->args.unused = args->unused; /* copies whole union */
else
icmph->args.unused = 0;
/* Link in a copy of the beginning of the original datagram */
data = copy_p(bp,dlen);
reply->next = data; /* Could be NULL if copy fails */
/* Compute ICMP checksum and send */
icmph->checksum = 0;
icmph->checksum = cksum(NULLHEADER,reply,length);
ip_send(ip_addr,ntohl(iph->source),ICMP_PTCL,iph->tos,0,reply,length,0,0);
}
#ifdef TRACE
/* ICMP message types */
char *icmptypes[] = {
"Echo Reply",
NULLCHAR,
NULLCHAR,
"Unreachable",
"Source Quench",
"Redirect",
NULLCHAR,
NULLCHAR,
"Echo Request",
NULLCHAR,
NULLCHAR,
"Time Exceeded",
"Parameter Problem",
"Timestamp",
"Timestamp Reply",
"Information Request",
"Information Reply"
};
/* ICMP unreachable messages */
char *unreach[] = {
"Network",
"Host",
"Protocol",
"Port",
"Fragmentation",
"Source route"
};
/* ICMP Time exceeded messages */
char *exceed[] = {
"Time-to-live",
"Fragment reassembly"
};
/* ICMP redirect messages */
char *redirect[] = {
"Network",
"Host",
"TOS & Network",
"TOS & Host"
};
int
doicmpstat(argc,argv)
int argc;
char *argv[];
{
extern struct icmp_errors icmp_errors;
extern struct icmp_stats icmp_stats;
register int i;
printf("chksum err %u no space %u icmp %u bdcsts %u\r\n",
icmp_errors.checksum,icmp_errors.nospace,icmp_errors.noloop,
icmp_errors.bdcsts);
printf("type rcvd sent\r\n");
for(i=0;i<ICMP_TYPES;i++){
if(icmp_stats.input[i] == 0 && icmp_stats.output[i] == 0)
continue;
printf("%-6u%-6u%-6u",i,icmp_stats.input[i],
icmp_stats.output[i]);
if(icmptypes[i] != NULLCHAR)
printf(" %s",icmptypes[i]);
printf("\r\n");
}
return 0;
}
/* Dump an ICMP header */
void
icmp_dump(bp,source,dest,check)
struct mbuf *bp;
int32 source,dest;
int check; /* If 0, bypass checksum verify */
{
register struct icmp *icmp;
char *codemsg;
struct mbuf *ibp;
int i;
char tmpbuf;
if(bp == NULLBUF)
return;
/* If packet isn't in a single buffer, make a temporary copy and
* note the fact so we free it later
*/
if(bp->next != NULLBUF){
bp = copy_p(bp,len_mbuf(bp));
tmpbuf = 1;
} else
tmpbuf = 0;
codemsg = NULLCHAR;
icmp = (struct icmp *)bp->data;
if(icmp->type <= 16 && icmptypes[icmp->type] != NULLCHAR)
printf("ICMP: %s",icmptypes[icmp->type]);
else
printf("ICMP: type %u",icmp->type);
switch(icmp->type){
case DEST_UNREACH:
if(icmp->code <= 5)
codemsg = unreach[icmp->code];
break;
case REDIRECT:
if(icmp->code <= 3)
codemsg = redirect[icmp->code];
break;
case TIME_EXCEED:
if(icmp->code <= 1)
codemsg = exceed[icmp->code];
break;
}
if(codemsg != NULLCHAR)
printf(" %s",codemsg);
else
printf(" code %u",icmp->code);
/* Special case for parameter problem message */
if(icmp->type == PARAM_PROB)
printf(" pointer = 0x%x",icmp->args.pointer);
if(check){
/* Verify checksum */
if((i = cksum(NULLHEADER,bp,len_mbuf(bp))) != 0)
printf(" CHECKSUM ERROR (%u)",i);
}
printf("\r\n");
/* Dump the offending IP header, if any */
switch(icmp->type){
case DEST_UNREACH:
case TIME_EXCEED:
case PARAM_PROB:
case QUENCH:
case REDIRECT:
printf("Returned ");
dup_p(&ibp,bp,sizeof(struct icmp),
len_mbuf(bp) - sizeof(struct icmp));
ip_dump(ibp);
free_p(ibp);
}
if(tmpbuf)
free_p(bp);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > mbuf.c
/* Primitive mbuf allocate/free routines */
#ifdef TRACE
#include <stdio.h>
#endif
#include "machdep.h"
#include "mbuf.h"
/* Allocate mbuf with associated buffer of 'size' bytes */
struct mbuf *
alloc_mbuf(size)
register int16 size;
{
register struct mbuf *bp;
char *malloc();
if((bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)))) == NULLBUF)
return NULLBUF;
bp->next = bp->anext = NULLBUF;
if(size != 0){
bp->data = (char *)(bp + 1);
} else {
bp->data = NULLCHAR;
}
bp->cnt = 0;
return bp;
}
/* Free all resources associated with mbuf
* Return pointer to next mbuf in packet chain
*/
struct mbuf *
free_mbuf(bp)
register struct mbuf *bp;
{
register struct mbuf *bp1 = NULLBUF;
if(bp != NULLBUF){
bp1 = bp->next;
free((char *)bp);
}
return bp1;
}
/* Free packet (a chain of mbufs). Return pointer to next packet on queue,
* if any
*/
struct mbuf *
free_p(bp)
register struct mbuf *bp;
{
struct mbuf *abp;
if(bp == NULLBUF)
return NULLBUF;
abp = bp->anext;
while(bp != NULLBUF)
bp = free_mbuf(bp);
return abp;
}
/* Free entire queue of packets (of mbufs) */
free_q(q)
struct mbuf **q;
{
register struct mbuf *bp;
while((bp = dequeue(q)) != NULLBUF)
free_p(bp);
}
/* Count up the total number of bytes in an mbuf */
int16
len_mbuf(bp)
register struct mbuf *bp;
{
int cnt;
cnt = 0;
while(bp != NULLBUF){
cnt += bp->cnt;
bp = bp->next;
}
return cnt;
}
/* Count up the number of packets in a queue */
int16
len_q(bp)
register struct mbuf *bp;
{
register int cnt;
for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
;
return cnt;
}
/* Duplicate/enqueue/dequeue operations based on mbufs */
/* Duplicate first 'cnt' bytes of packet starting at 'offset'.
* This is done without copying data; only the headers are duplicated,
* but without data segments of their own. The pointers are set up to
* share the data segments of the original copy. The return pointer is
* passed back through the first argument, and the return value is the
* number of bytes actually duplicated.
*/
int16
dup_p(hp,bp,offset,cnt)
struct mbuf **hp;
register struct mbuf *bp;
register int16 offset;
register int16 cnt;
{
register struct mbuf *cp;
int16 tot;
if(cnt == 0 || bp == NULLBUF || hp == (struct mbuf **)NULL){
if(hp != (struct mbuf **)NULL)
*hp = NULLBUF;
return 0;
}
if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
return 0;
}
/* Skip over leading mbufs that are smaller than the offset */
while(bp != NULLBUF && bp->cnt <= offset){
offset -= bp->cnt;
bp = bp->next;
}
if(bp == NULLBUF){
free_mbuf(cp);
*hp = NULLBUF;
return 0; /* Offset was too big */
}
tot = 0;
for(;;){
cp->data = bp->data + offset;
cp->cnt = min(cnt,bp->cnt - offset);
offset = 0;
cnt -= cp->cnt;
tot += cp->cnt;
bp = bp->next;
if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
break;
cp = cp->next;
}
return tot;
}
/* Copy first 'cnt' bytes of packet into a new, single mbuf */
struct mbuf *
copy_p(bp,cnt)
register struct mbuf *bp;
register int16 cnt;
{
register struct mbuf *cp;
register char *wp;
register int16 n;
if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
return NULLBUF;
wp = cp->data;
while(cnt != 0 && bp != NULLBUF){
n = min(cnt,bp->cnt);
bcopy(bp->data,wp,n);
wp += n;
cp->cnt += n;
cnt -= n;
bp = bp->next;
}
return cp;
}
/* Copy and delete "cnt" bytes from beginning of packet. Return number of
* bytes actually pulled off
*/
int16
pullup(bph,buf,cnt)
struct mbuf **bph;
char *buf;
int16 cnt;
{
register struct mbuf *bp;
int16 n,tot;
tot = 0;
if(bph == (struct mbuf **)NULL)
return 0;
while(*bph != NULLBUF && cnt != 0){
bp = *bph;
n = min(cnt,bp->cnt);
if(buf != NULLCHAR){
bcopy(bp->data,buf,n);
buf += n;
}
tot += n;
cnt -= n;
bp->data += n;
bp->cnt -= n;
if(bp->cnt == 0){
*bph = free_mbuf(bp);
}
}
return tot;
}
/* Append mbuf to end of mbuf chain */
int16
append(bph,bp)
struct mbuf **bph;
struct mbuf *bp;
{
register struct mbuf *p;
if(bph == (struct mbuf **)NULL || bp == NULLBUF)
return;
if(*bph == NULLBUF){
/* First one on chain */
*bph = bp;
} else {
for(p = *bph ; p->next != NULLBUF ; p = p->next)
;
p->next = bp;
}
}
/* Append packet to end of packet queue */
void
enqueue(q,bp)
struct mbuf **q;
struct mbuf *bp;
{
register struct mbuf *p;
char i_state;
if(q == (struct mbuf **)NULL || bp == NULLBUF)
return;
i_state = disable();
if(*q == NULLBUF){
/* List is empty, stick at front */
*q = bp;
} else {
for(p = *q ; p->anext != NULLBUF ; p = p->anext)
;
p->anext = bp;
}
restore(i_state);
}
/* Unlink a packet from the head of the queue */
struct mbuf *
dequeue(q)
register struct mbuf **q;
{
register struct mbuf *bp;
char i_state;
if(q == (struct mbuf **)NULL)
return NULLBUF;
i_state = disable();
if((bp = *q) != NULLBUF){
*q = bp->anext;
bp->anext = NULLBUF;
}
restore(i_state);
return bp;
}
/* Copy user data into an mbuf */
struct mbuf *
qdata(data,cnt)
char *data;
int16 cnt;
{
register struct mbuf *bp;
if((bp = alloc_mbuf(cnt)) == NULLBUF)
return NULLBUF;
bcopy(data,bp->data,cnt);
bp->cnt = cnt;
return bp;
}
/* Copy mbuf data into user buffer */
int16
dqdata(bp,buf,cnt)
struct mbuf *bp;
char *buf;
unsigned cnt;
{
unsigned n,tot;
struct mbuf *bp1;
if(buf == NULLCHAR)
return 0;
tot = 0;
for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
n = min(bp1->cnt,cnt);
bcopy(bp1->data,buf,n);
cnt -= n;
buf += n;
tot += n;
}
free_p(bp);
return tot;
}
#ifdef TRACE
/* Comment this out if your library already has this function */
#define isprint(c) ((c) >= ' ' && (c) < 0x7f) /* Assumes ASCII */
/* Dump an mbuf in hex */
void
hexdump(bp)
struct mbuf *bp;
{
register struct mbuf *tbp;
int16 n;
int16 address;
void fmtline();
if(bp == NULLBUF)
return;
tbp = copy_p(bp,len_mbuf(bp));
address = 0;
while(tbp->cnt != 0){
n = min(tbp->cnt,16);
fmtline(address,tbp->data,n);
address += n;
tbp->data += n;
tbp->cnt -= n;
}
free_p(tbp);
fflush(stdout);
}
/* Dump an mbuf in ascii */
void
asciidump(bp)
struct mbuf *bp;
{
struct mbuf *tbp;
char c;
int16 tot;
if(bp == NULLBUF)
return;
tbp = copy_p(bp,len_mbuf(bp));
tot = 0;
while(pullup(&tbp,&c,1) == 1){
if((tot % 64) == 0)
printf("%04x ",tot);
if(isprint(c))
putchar(c);
else
putchar('.');
tot++;
if((tot % 64) == 0)
printf("\r\n");
}
if((tot % 64) != 0)
printf("\r\n");
free_p(tbp);
fflush(stdout);
}
/* Print a buffer up to 16 bytes long in formatted hex with ascii
* translation, e.g.,
* 0000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?
*/
void
fmtline(addr,buf,len)
int16 addr;
char *buf;
int16 len;
{
char line[80];
char *aptr,*cptr;
int16 c;
void ctohex();
for(cptr = line;cptr < &line[80];cptr++)
*cptr = ' ';
ctohex(line,(int16)hibyte(addr));
ctohex(line+2,(int16)lobyte(addr));
aptr = &line[6];
cptr = &line[55];
while(len-- != 0){
c = *buf++ & 0xff;
ctohex(aptr,c);
aptr += 3;
c &= 0x7f;
if(isprint(c)){
*cptr++ = c;
} else {
*cptr++ = '.';
}
}
*cptr++ = '\r';
*cptr++ = '\n';
#ifdef AMIGA
*cptr = '\0';
printf(line);
#else
fwrite(line,1,(unsigned)(cptr-line),stdout);
#endif
}
/* Convert byte to two ascii-hex characters */
static
void
ctohex(buf,c)
char *buf;
int16 c;
{
static char hex[] = "0123456789abcdef";
buf[0] = hex[hinibble(c)];
buf[1] = hex[lonibble(c)];
}
#endif
SHAR_EOF
cat << \SHAR_EOF > slip.c
/* Send and receive IP datagrams on serial lines. Compatible with SLIP
* under Berkeley Unix.
*/
#ifdef TRACE
#include <stdio.h>
#endif
#include "machdep.h"
#ifdef TRACE
#include "trace.h"
#endif
#include "mbuf.h"
#include "iface.h"
#include "ax25.h"
#include "slip.h"
#ifdef AMIGA
#include "amiga.h"
#else
#include "pc.h"
#endif
int slip_send();
int doslip();
int asy_output();
/* Slip level control structure */
struct slip slip[ASY_MAX];
unsigned nasy;
/* Send routine for point-to-point slip
* This is a trivial function since there is no slip link-level header
*/
int
slip_send(bp,interface,gateway,precedence,delay,throughput,reliability)
struct mbuf *bp; /* Buffer to send */
struct interface *interface; /* Pointer to interface control block */
int32 gateway; /* Ignored (SLIP is point-to-point) */
char precedence;
char delay;
char throughput;
char reliability;
{
/* Queue a frame on the slip output queue and start transmitter */
if(interface == NULLIF){
free_p(bp);
return;
}
#ifdef TRACE
if(trace & TRACE_SLIP){
printf("%s sent:\r\n",interface->name);
if((trace & TRACE_HDR) > 2)
ip_dump(bp);
if(trace & TRACE_DUMP)
hexdump(bp);
if(trace & TRACE_ASCII)
asciidump(bp);
fflush(stdout);
}
#endif
slipq(interface->dev,bp);
}
/* Encode a packet in slip framing, put on link output queue, and kick
* transmitter
*/
slipq(dev,bp)
int16 dev; /* Serial line number */
struct mbuf *bp; /* Buffer to be sent */
{
register struct slip *sp;
struct mbuf *slip_encode();
if((bp = slip_encode(bp)) == NULLBUF)
return;
sp = &slip[dev];
enqueue(&sp->sndq,bp);
sp->sndcnt++;
if(sp->tbp == NULLBUF)
asy_start(dev);
}
/* Start output, if possible, on asynch device dev */
static
asy_start(dev)
int16 dev;
{
register struct slip *sp;
if(!stxrdy(dev))
return; /* Transmitter not ready */
sp = &slip[dev];
if(sp->tbp != NULLBUF){
/* transmission just completed */
free_p(sp->tbp);
sp->tbp = NULLBUF;
}
if(sp->sndq == NULLBUF)
return; /* No work */
sp->tbp = dequeue(&sp->sndq);
sp->sndcnt--;
asy_output(dev,sp->tbp->data,sp->tbp->cnt);
}
/* Encode a packet in SLIP format */
static
struct mbuf *
slip_encode(bp)
struct mbuf *bp;
{
struct mbuf *lbp; /* Mbuf containing line-ready packet */
register char *cp;
register int cnt;
char c;
/* Allocate output mbuf that's twice as long as the packet.
* This is a worst-case guess (consider a packet full of FR_ENDs!)
*/
lbp = alloc_mbuf(2*len_mbuf(bp) + 2);
if(lbp == NULLBUF){
/* No space; drop */
free_p(bp);
return NULLBUF;
}
cp = lbp->data;
cnt = 0;
/* Flush out any line garbage */
*cp++ = FR_END;
cnt++;
/* Copy input to output, escaping special characters */
while(pullup(&bp,&c,1) == 1){
switch(c & 0xff){
case FR_ESC:
*cp++ = FR_ESC;
*cp++ = T_FR_ESC;
cnt += 2;
break;
case FR_END:
*cp++ = FR_ESC;
*cp++ = T_FR_END;
cnt += 2;
break;
default:
*cp++ = c;
cnt++;
}
}
*cp++ = FR_END;
cnt++;
lbp->cnt = cnt;
return lbp;
}
/* Process incoming bytes in SLIP format
* When a buffer is complete, return it; otherwise NULLBUF
*/
static
struct mbuf *
slip_decode(dev,c)
int dev; /* Slip unit number */
char c; /* Incoming character */
{
struct mbuf *bp,*nbp;
register struct slip *sp;
sp = &slip[dev];
switch(c & 0xff){
case FR_END:
if(sp->rbp != NULLBUF){
/* Kick upstairs */
bp = sp->rbp;
sp->rbp = NULLBUF;
sp->rcnt = 0;
/* Copy into contiguous buffer, if necessary */
if(bp->next != NULLBUF){
nbp = copy_p(bp,len_mbuf(bp));
free_p(bp);
bp = nbp;
}
return bp;
}
return NULLBUF;
case FR_ESC:
sp->escaped = 1;
return NULLBUF;
}
if(sp->escaped){
sp->escaped = 0;
switch(c & 0xff){
case T_FR_ESC:
c = FR_ESC;
break;
case T_FR_END:
c = FR_END;
break;
default:
sp->errors++;
}
}
if(sp->rcnt == SLIP_MTU){
/* Packet is too large, drop it and start over */
free_p(sp->rbp);
sp->rbp = NULLBUF;
sp->rcnt = 0;
return NULLBUF;
}
/* We reach here with a character for the buffer;
* make sure there's space for it
*/
if(sp->rbp == NULLBUF){
/* Allocate first mbuf for new packet */
if((sp->rbp1 = sp->rbp = alloc_mbuf(SLIP_ALLOC)) == NULLBUF)
return NULLBUF; /* No memory, drop */
sp->rcp = sp->rbp->data;
} else if(sp->rbp1->cnt == SLIP_ALLOC){
/* Current mbuf is full; link in another */
if((sp->rbp1->next = alloc_mbuf(SLIP_ALLOC)) == NULLBUF){
/* No memory, drop whole thing */
free_p(sp->rbp);
sp->rbp = NULLBUF;
sp->rcnt = 0;
return NULLBUF;
}
sp->rbp1 = sp->rbp1->next;
sp->rcp = sp->rbp1->data;
}
/* Store the character, increment fragment and total
* byte counts
*/
*sp->rcp++ = c;
sp->rbp1->cnt++;
sp->rcnt++;
return NULLBUF;
}
/* Process SLIP line I/O */
int
doslip(interface)
struct interface *interface;
{
char c;
struct mbuf *bp;
int16 dev;
dev = interface->dev;
/* Process any pending input */
while(asy_recv(dev,&c,1) != 0)
if((bp = slip_decode(dev,c)) != NULLBUF)
(*slip[dev].recv)(interface,bp);
/* Kick the transmitter if it's idle */
if(stxrdy(dev))
asy_start(dev);
}
/* Unwrap incoming SLIP packets -- trivial operation since there's no
* link level header
*/
slip_recv(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
#ifdef TRACE
if(trace & TRACE_SLIP){
printf("%s recv:\r\n",interface->name);
if((trace & TRACE_HDR) > 2)
ip_dump(bp);
if(trace & TRACE_DUMP)
hexdump(bp);
if(trace & TRACE_ASCII)
asciidump(bp);
fflush(stdout);
}
#endif
ip_route(bp,0);
}
/* Attach a serial interface to the system
* argv[0]: hardware type, must be "asy"
* argv[1]: I/O address, e.g., "0x3f8"
* argv[2]: vector, e.g., "4"
* argv[3]: mode, may be:
* "slip" (point-to-point SLIP)
* "ax25" (AX.25 UI frame format in SLIP for raw TNC)
* argv[4]: interface label, e.g., "sl0"
* argv[5]: receiver ring buffer size in bytes
* argv[6]: maximum transmission unit, bytes
* argv[7]: interface speed, e.g, "9600"
*/
asy_attach(argc,argv)
int argc;
char *argv[];
{
register struct interface *if_asy;
extern struct interface *ifaces;
int dev;
char *malloc(), *calloc();
int asy_init();
int asy_send();
int doslip();
int asy_stop();
int ax_send();
int kiss_recv();
int kiss_output();
if(nasy >= ASY_MAX){
printf("Too many asynch controllers\r\n");
return -1;
}
dev = nasy++;
/* Initialize hardware-level control structure */
asy[dev].addr = htoi(argv[1]);
asy[dev].vec = htoi(argv[2]);
/* Create interface structure and fill in details */
if_asy = (struct interface *)calloc(1, sizeof(struct interface));
if_asy->name = malloc(strlen(argv[4])+1);
strcpy(if_asy->name,argv[4]);
if_asy->mtu = atoi(argv[6]);
if_asy->dev = dev;
if_asy->recv = doslip;
if_asy->stop = asy_stop;
if(strcmp(argv[3],"slip") == 0){
if_asy->send = slip_send;
if_asy->output = NULLFP;
if_asy->flags = 0;
slip[dev].recv = slip_recv;
}
else if(strcmp(argv[3],"ax25") == 0){
if_asy->send = ax_send;
if_asy->output = kiss_output;
if_asy->flags = IF_BROADCAST;
if(if_asy->hwaddr == NULLCHAR)
if_asy->hwaddr = malloc(AXALEN);
bcopy((char *)&mycall,if_asy->hwaddr,AXALEN);
slip[dev].recv = kiss_recv;
}
else {
printf("Mode %s unknown for interface %s\r\n",
argv[3],argv[4]);
free((char *)if_asy);
return -1;
}
if_asy->next = ifaces;
ifaces = if_asy;
asy_init(dev,(unsigned)atoi(argv[5]));
asy_speed(dev,atoi(argv[7]));
}
SHAR_EOF
cat << \SHAR_EOF > smtpcli.c
/* smtpcli.c
* Client routines for Simple Mail Transfer Protocol ala RFC821
* A.D. Barksdale Garbee II, aka Bdale, N3EUA
* Copyright 1986 Bdale Garbee, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*/
#include <stdio.h>
#include "machdep.h"
#include "netuser.h"
#include "mbuf.h"
#include "timer.h"
#include "tcp.h"
#include "smtp.h"
extern int16 lport; /* local port placeholder */
int32 aton();
static void sendit();
static struct timer smtpcli_t;
char *index(),*rindex();
/* init routine called when program fired up */
smtpclinit()
{
int dosmtptick();
smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */
smtpcli_t.arg = 0; /* dummy value */
smtpcli_t.start = SMTPCLITIME; /* set timer duration */
start_timer(&smtpcli_t); /* and fire it up */
}
/* this is the routine that gets called every so often to do outgoing mail
processing */
int
dosmtptick()
{
char lfilename[LINELEN],
tmpstring[LINELEN],
wfilename[13],
*ptr;
FILE *lfile;
struct smtp_msg *mp;
struct socket lsocket, fsocket;
char *calloc(),*malloc();
void smtp_rec(), smtp_cts(), smtp_state();
/* printf("DOSMTPTICK() entered\n"); */
lsocket.address = ip_addr; /* our ip address */
fsocket.port = SMTP_PORT;
/* if lock file exists in mqueue dir, return */
sprintf(lfilename,"%s%s",MAILQDIR,"lockfile");
if ((lfile = fopen(lfilename,"x")) == NULL)
return;
/* get next work filename from mqueue directory */
sprintf(tmpstring,"%s%s",MAILQDIR,"*.wrk");
#ifndef AMIGA
filedir(tmpstring,0,wfilename);
#endif
if (wfilename[0] == '\0')
return; /* no work to be done */
/* if we have work, rebuild the exact (non-wild) filename */
mp = (struct smtp_msg *)calloc(1,sizeof (struct smtp_msg));
sprintf(tmpstring,"%s%s",MAILQDIR,wfilename);
ptr = &tmpstring[0];
mp->filename = malloc((unsigned)strlen(ptr)+1);
strcpy(mp->filename,ptr);
/* printf("work file name: %s\n",mp->filename); /* debug only */
mp->wfile = fopen(mp->filename,"r");
/* get ip address, from stuff, to stuff */
fgets(tmpstring,LINELEN,mp->wfile); /* read target ip addr */
/* printf("target ip addr: %s\n",tmpstring); */
fgets(mp->toaddr,LINELEN,mp->wfile); /* who to */
rip(mp->toaddr);
/* printf("addressee: %s\n",mp->toaddr); */
fgets(mp->fromaddr,LINELEN,mp->wfile); /* who from */
rip(mp->fromaddr);
/* printf("sender: %s\n",mp->fromaddr); */
fclose(mp->wfile);
/* set up the rest of the socket info from what we got */
fsocket.address = aton(tmpstring); /* destination ip address */
lsocket.port = lport++; /* next unused port */
/* open smtp connection */
mp->state = CLI_OPEN_STATE; /* init state placeholder */
/* printf("Opening TCP connection for SMTP client\n"); */
mp->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,1024,
smtp_rec,smtp_cts,smtp_state,0,(int *)mp);
mp->tcb->user = (int *)mp; /* Upward pointer */
/* printf("releasing lock\n"); */
if (lfile != NULL) { /* release lock */
fclose(lfile);
unlink(lfilename);
}
}
/* replace terminating end of line marker(s) with null */
rip(s)
char *s;
{
char *c;
c=s;
while (*c != '\0') {
switch (*c) {
case '\r':
case '\n':
*c='\0';
break;
default:
c++;
break;
}
}
}
/* this is the master state machine that handles a single SMTP transaction */
smtp_transaction(mp)
struct smtp_msg *mp;
{
char tmpstring[LINELEN]; /* where we build command lines */
/* printf("SMTP_TRANSACTION() called, state=%u\n",mp->state); */
if (affirmative(mp)) {
switch(mp->state) {
case CLI_OPEN_STATE:
mp->state = CLI_MAIL_STATE;
/* issue MAIL command */
/* printf("FROMADDR = %s\n",mp->fromaddr); */
sprintf(tmpstring,"mail from:<%s>\r\n",mp->fromaddr);
sendit(mp,tmpstring);
break;
case CLI_MAIL_STATE:
mp->state = CLI_RCPT_STATE;
/* issue RCPT command */
sprintf(tmpstring,"rcpt to:<%s>\r\n",mp->toaddr);
sendit(mp,tmpstring);
break;
case CLI_RCPT_STATE:
mp->state = CLI_SEND_STATE;
/* open text file */
strcpy(tmpstring,mp->filename);
strcpy(index(tmpstring,'.'),".txt");
/* printf("text filename: %s",tmpstring); */
mp->tfile = fopen(tmpstring,"r");
/* issue DATA command */
sprintf(tmpstring,"data\r\n");
sendit(mp,tmpstring);
break;
case CLI_SEND_STATE:
/* the transmitter upcall routine will advance the
state pointer on end of file, so we do nada... */
break;
case CLI_UNLK_STATE:
unlink(mp->filename); /* unlink workfile */
/* close and unlink the textfile */
fclose(mp->tfile);
strcpy(tmpstring,mp->filename);
strcpy(index(tmpstring,'.'),".txt");
unlink(tmpstring);
mp->state = CLI_QUIT_STATE;
/* issue a quit command */
sprintf(tmpstring,"quit\r\n");
sendit(mp,tmpstring);
break;
case CLI_QUIT_STATE:
/* either start next transaction, or quit */
close_tcp(mp->tcb); /* close up connection */
break;
}
} else { /* if we get here, means we got a negative reply */
/* for the moment, just let that hose us... */
mp->state = CLI_QUIT_STATE;
/* issue a quit command */
sprintf(tmpstring,"quit\r\n");
sendit(mp,tmpstring);
}
}
/* return true if the passed string contains a positive response code */
affirmative(mp)
struct smtp_msg *mp;
{
/* 2 is always good, 3 is ok if we've just sent 'data' command */
if ((*mp->buf = '2') ||
((*mp->buf = '3') && (mp->state = CLI_DATA_STATE)))
return 1;
else return 0;
}
/* smtp receiver upcall routine. fires up the state machine to parse input */
static
void
smtp_rec(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
register struct smtp_msg *mp;
char *inet_ntoa(), c;
struct mbuf *bp;
/* may want a void line here for procedures used */
/* printf("SMTP_REC called\n"); */
mp = (struct smtp_msg *)tcb->user; /* point to our struct */
recv_tcp(tcb,&bp,cnt); /* suck up chars from low level routine */
/* Assemble input line in buffer, return if incomplete */
while(pullup(&bp,&c,1) == 1) {
switch(c) {
case '\r': /* strip cr's */
continue;
case '\n': /* line is finished, go do it! */
mp->buf[mp->cnt] = '\0';
smtp_transaction(mp);
break;
default: /* other chars get added to buffer */
mp->buf[mp->cnt++] = c;
break;
}
}
}
/* smtp transmitter ready upcall routine. twiddles cts flag */
static
void
smtp_cts(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
register struct smtp_msg *mp;
struct mbuf *bp;
char tmpstring[LINELEN];
char *cp;
int c;
/* printf("SMTP_CTS called\n"); */
mp = (struct smtp_msg *)tcb->user; /* point to our struct */
/* don't do anything until/unless we're supposed to be sending */
if(mp->state != CLI_SEND_STATE) return;
if((bp = alloc_mbuf(cnt)) == NULLBUF){
/* Hard to know what to do here */
return;
}
cp = bp->data;
while(cnt > 1 && (c = getc(mp->tfile)) != EOF){
*cp++ = c;
bp->cnt++;
cnt--;
}
if(bp->cnt != 0)
send_tcp(tcb,bp);
else
free_p(bp);
if(cnt > 1){ /* EOF seen */
sprintf(tmpstring,"\r\n.\r\n");
sendit(mp,tmpstring);
mp->state = CLI_UNLK_STATE;
}
}
/* smtp state change upcall routine. cans connection on error */
static
void
smtp_state(tcb,old,new)
struct tcb *tcb;
char old,new;
{
struct smtp_msg *mp;
/* printf("SMTP_STATE called, state=%u\n",new); */
mp = (struct smtp_msg *)tcb->user;
switch(new) {
case ESTABLISHED:
mp->state = CLI_OPEN_STATE; /* shouldn't be needed */
break;
case CLOSE_WAIT:
close_tcp(tcb); /* shut things down */
/* may want to do something here to shut down
the rest of the transaction? */
break;
case CLOSED:
del_tcp(tcb); /* hosed for good */
if(mp->filename != NULLCHAR)
free(mp->filename);
free((char *)mp);
break;
}
}
/* Send message back to server */
static
void
sendit(mp,message)
struct smtp_msg *mp;
char *message;
{
struct mbuf *bp,*qdata();
/* printf("SENDIT called: %s",message); */
bp = qdata(message,(int16)strlen(message));
send_tcp(mp->tcb,bp);
}
SHAR_EOF
cat << \SHAR_EOF > tcpout.c
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
/* Send a segment on the specified connection. One gets sent only
* if there is data to be sent or if "force" is non zero
*/
void
tcp_output(tcb)
register struct tcb *tcb;
{
struct pseudo_header ph;
struct mbuf *hbp;
int16 hsize; /* Size of header */
struct tcp_header *tcph;
struct mss *mssp;
int16 ssize; /* Size of current segment being sent,
* including SYN and FIN flags */
int16 dsize; /* Size of segment less SYN and FIN */
int16 usable; /* Usable window */
int16 sent; /* Sequence count (incl SYN/FIN) already in the pipe */
if(tcb == NULLTCB)
return;
switch(tcb->state){
case LISTEN:
case CLOSED:
return; /* Don't send anything */
}
for(;;){
sent = tcb->snd.ptr - tcb->snd.una;
/* If this is a retransmission, send only the oldest segment
* (first-only retransmission policy)
*/
if(tcb->retry != 0 && sent != 0)
break;
/* There can only be one outstanding segment in this state
* since the other end would reject any segment without SYN
*/
if(tcb->state == SYN_SENT && sent != 0)
break;
if(tcb->snd.wnd == 0){
/* Allow only one closed-window probe at a time */
if(sent != 0)
break;
/* Force a closed-window probe */
usable = 1;
} else {
/* usable window = offered window - unacked bytes in transit */
usable = tcb->snd.wnd - sent;
/* John Nagle's "single outstanding segment" rule.
* Allow only one segment in the pipeline unless there is enough
* unsent data to form at least one maximum-sized segment.
*/
if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
usable = 0;
}
/* Silly window avoidance. Don't send anything if the usable window
* is less than a quarter of the offered window.
* This test comes into play only when the offered window is at
* least 4 times the MSS; otherwise Nagle's test is sufficient
* to prevent SWS.
*/
else if(usable < tcb->snd.wnd/4){
usable = 0;
}
}
/* Compute size of segment to send. This is either the usable
* window, the mss, or the amount we have on hand, whichever is less.
* (I don't like optimistic windows)
*/
ssize = min(tcb->sndcnt - sent,usable);
ssize = min(ssize,tcb->mss);
dsize = ssize;
if(ssize == 0 && tcb->force == 0)
break; /* No need to send anything */
/* Determine size of TCP header and allocate mbuf.
* If sending SYN, allow space for the MSS option
*/
switch(tcb->state){
case SYN_SENT:
case SYN_RECEIVED:
hsize = sizeof(struct tcp_header) + sizeof(struct mss);
break;
default:
hsize = sizeof(struct tcp_header);
break;
}
if((hbp = alloc_mbuf(hsize)) == NULLBUF)
break; /* No room to form a packet */
tcb->force = 0; /* Only one forced segment! */
hbp->cnt = hsize;
tcph = (struct tcp_header *)hbp->data;
tcph->source = htons(tcb->conn.local.port);
tcph->dest = htons(tcb->conn.remote.port);
tcph->offset = hsize/sizeof(int32) << DSHIFT;
tcph->flags = 0;
/* Set the SYN and ACK flags according to the state we're in. It is
* assumed that if this segment is associated with a state transition,
* then the state change will already have been made. This allows
* this routine to be called from a retransmission timeout with
* force=1.
* If SYN is being sent, adjust the dsize counter so we'll
* try to get the right amount of data off the send queue.
*/
switch(tcb->state){
case SYN_SENT:
case SYN_RECEIVED:
if(tcb->snd.ptr == tcb->iss){
tcph->flags = SYN;
dsize--;
}
/* Also send MSS */
mssp = (struct mss *)(tcph + 1);
mssp->kind = MSS_KIND;
mssp->length = MSS_LENGTH;
mssp->mss = htons(tcp_mss);
}
switch(tcb->state){
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
case FINWAIT2:
case TIME_WAIT:
case CLOSING:
case FINWAIT1:
case LAST_ACK:
tcph->flags |= ACK;
break;
}
tcph->seq = htonl(tcb->snd.ptr);
tcph->ack = htonl(tcb->rcv.nxt);
tcph->wnd = htons(tcb->rcv.wnd);
tcph->checksum = 0;
tcph->up = 0;
/* Now try to extract some data from the send queue.
* Since SYN and FIN occupy sequence space and are reflected
* in sndcnt but don't actually sit in the send queue,
* dup_p will return one less than dsize if a FIN needs to be sent.
*/
if(dsize != 0){
if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){
/* We ran past the end of the send queue; send a FIN */
tcph->flags |= FIN;
dsize--;
}
}
/* If the entire send queue will now be in the pipe, set the
* push flag
*/
if(dsize != 0 && sent + ssize == tcb->sndcnt)
tcph->flags |= PSH;
tcb->snd.ptr += ssize;
/* If this is the first transmission of a range of sequence
* numbers, record it so we'll accept acknowledgments
* for it later
*/
if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
tcb->snd.nxt = tcb->snd.ptr;
/* Fill in fields of pseudo IP header */
ph.source = tcb->conn.local.address;
ph.dest = tcb->conn.remote.address;
ph.protocol = TCP_PTCL;
ph.zero = 0;
ph.length = hsize + dsize;
/* Compute checksum over pseudo-header, TCP header and data,
* and pass it off to IP
*/
tcph->checksum = cksum(&ph,hbp,ph.length);
/* If we're sending some data or flags, start retransmission
* timer if it isn't already running.
*/
if(ssize != 0){
if(tcb->timer.state != TIMER_RUN){
/* Never initialize the timer with zero; it won't run! */
tcb->timer.start = max(tcb->timer.start,1);
start_timer(&tcb->timer);
}
/* If round trip timer isn't running, start it */
if(seq_ge(tcb->snd.una,tcb->rttseq))
tcb->rttseq = tcb->snd.ptr;
}
ip_send(tcb->conn.local.address,tcb->conn.remote.address,
TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
}
}
SHAR_EOF
cat << \SHAR_EOF > tcpuser.c
/* User calls to TCP */
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "ip.h"
#include "tcp.h"
int16 tcp_window = DEF_WND;
struct tcb *
open_tcp(lsocket,fsocket,active,window,r_upcall,t_upcall,s_upcall,tos,user)
struct socket *lsocket; /* Local socket */
struct socket *fsocket; /* Remote socket */
int active; /* Active/passive */
int16 window; /* Receive window (and send buffer) sizes */
void (*r_upcall)(); /* Function to call when data arrives */
void (*t_upcall)(); /* Function to call when ok to send more data */
void (*s_upcall)(); /* Function to call when connection state changes */
char tos;
int *user; /* User linkage area */
{
struct connection conn;
register struct tcb *tcb;
void send_syn();
if(lsocket == NULLSOCK){
net_error = INVALID;
return NULLTCB;
}
conn.local.address = lsocket->address;
conn.local.port = lsocket->port;
if(fsocket != NULLSOCK){
conn.remote.address = fsocket->address;
conn.remote.port = fsocket->port;
} else {
conn.remote.address = 0;
conn.remote.port = 0;
}
if((tcb = lookup_tcb(&conn)) == NULLTCB){
if((tcb = create_tcb(&conn)) == NULLTCB){
net_error = NO_SPACE;
return NULLTCB;
}
} else if(tcb->state != LISTEN){
net_error = CON_EXISTS;
return NULLTCB;
}
tcb->user = user;
if(window != 0)
tcb->window = tcb->rcv.wnd = window;
else
tcb->window = tcb->rcv.wnd = tcp_window;
tcb->r_upcall = r_upcall;
tcb->t_upcall = t_upcall;
tcb->s_upcall = s_upcall;
tcb->tos = tos;
if(!active){
setstate(tcb,LISTEN);
return tcb;
}
/* Send SYN, go into SYN_SENT state */
send_syn(tcb);
setstate(tcb,SYN_SENT);
tcp_output(tcb);
tcp_stat.conout++;
return tcb;
}
/* User send routine */
int
send_tcp(tcb,bp)
register struct tcb *tcb;
struct mbuf *bp;
{
int16 cnt;
if(tcb == NULLTCB || bp == NULLBUF){
free_p(bp);
net_error = INVALID;
return -1;
}
cnt = len_mbuf(bp);
#ifdef TIGHT
/* If this would overfill our send queue, reject it entirely */
if(tcb->sndcnt + cnt > tcb->window){
free_p(bp);
net_error = WOULDBLK;
return -1;
}
#endif
switch(tcb->state){
case CLOSED:
free_p(bp);
net_error = NO_CONN;
return -1;
case LISTEN: /* Change state from passive to active */
send_syn(tcb);
setstate(tcb,SYN_SENT); /* Note fall-thru */
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
tcp_output(tcb);
break;
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
free_p(bp);
net_error = CON_CLOS;
return -1;
}
return cnt;
}
/* User receive routine */
int
recv_tcp(tcb,bp,cnt)
register struct tcb *tcb;
struct mbuf **bp;
int16 cnt;
{
if(tcb == NULLTCB || bp == (struct mbuf **)NULL){
net_error = INVALID;
return -1;
}
/* cnt == 0 means "I want it all" */
if(cnt == 0)
cnt = tcb->rcvcnt;
/* If there's something on the queue, just return it regardless
* of the state we're in.
*/
if(tcb->rcvcnt != 0){
/* See if the user can take all of it */
if(tcb->rcvcnt <= cnt){
cnt = tcb->rcvcnt;
*bp = tcb->rcvq;
tcb->rcvq = NULLBUF;
} else {
if((*bp = alloc_mbuf(cnt)) == NULLBUF){
net_error = NO_SPACE;
return -1;
}
pullup(&tcb->rcvq,(*bp)->data,cnt);
(*bp)->cnt = cnt;
}
tcb->rcvcnt -= cnt;
tcb->rcv.wnd += cnt;
/* Do a window update if it was closed */
if(cnt == tcb->rcv.wnd){
tcb->force = 1;
tcp_output(tcb);
}
return cnt;
} else {
/* If there's nothing on the queue, our action depends on what state
* we're in (i.e., whether or not we're expecting any more data).
* If no more data is expected, then simply return 0; this is
* interpreted as "end of file".
*/
switch(tcb->state){
case LISTEN:
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case FINWAIT1:
case FINWAIT2:
*bp = NULLBUF;
net_error = WOULDBLK;
return -1;
case CLOSED:
case CLOSE_WAIT:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
*bp = NULLBUF;
return 0;
}
}
return 0; /* Not reached, but lint doesn't know that */
}
/* This really means "I have no more data to send". It only closes the
* connection in one direction, and we can continue to receive data
* indefinitely.
*/
int
close_tcp(tcb)
register struct tcb *tcb;
{
if(tcb == NULLTCB){
net_error = INVALID;
return -1;
}
switch(tcb->state){
case LISTEN:
case SYN_SENT:
close_self(tcb,NORMAL);
return 0;
case SYN_RECEIVED:
case ESTABLISHED:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,FINWAIT1);
tcp_output(tcb);
return 0;
case CLOSE_WAIT:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,LAST_ACK);
tcp_output(tcb);
return 0;
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
net_error = CON_CLOS;
return -1;
}
return -1; /* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
* not in the CLOSED state. This function should normally be called by the
* user only in response to a state change upcall to CLOSED state.
*/
int
del_tcp(tcb)
register struct tcb *tcb;
{
void unlink_tcb();
struct reseq *rp,*rp1;
if(tcb == NULLTCB){
net_error = INVALID;
return -1;
}
unlink_tcb(tcb);
stop_timer(&tcb->timer);
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
free_p(rp->bp);
free((char *)rp);
}
tcb->reseq = NULLRESEQ;
free_p(tcb->rcvq);
free_p(tcb->sndq);
free((char *)tcb);
return 0;
}
/* Do printf on a tcp connection */
/*VARARGS*/
tprintf(tcb,message,arg1,arg2)
struct tcb *tcb;
char *message,*arg1,*arg2;
{
struct mbuf *bp;
int16 len;
char *cp,*index();
if(tcb == NULLTCB)
return 0;
len = strlen(message) + 10; /* fudge factor */
if((cp = index(message,'%')) != NULLCHAR){
/* What a gross hack! */
len += strlen(arg1);
if((cp = index(cp+1,'%')) != NULLCHAR){
/* I don't believe I'm writing this */
len += strlen(arg2);
}
}
bp = alloc_mbuf(len);
len = sprintf(bp->data,message,arg1,arg2);
bp->cnt = strlen(bp->data);
send_tcp(tcb,bp);
return len;
}
SHAR_EOF
cat << \SHAR_EOF > telnet.c
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "timer.h"
#include "internet.h"
#include "icmp.h"
#include "netuser.h"
#include "tcp.h"
#include "telnet.h"
#include "session.h"
extern char nospace[];
int refuse_echo = 0;
int unix_line_mode = 0; /* if true turn <cr> to <nl> when in line mode */
#ifdef DEBUG
char *t_options[] = {
"Transmit Binary",
"Echo",
"",
"Suppress Go Ahead",
"",
"Status",
"Timing Mark"
};
#endif
/* Execute user telnet command */
int
dotelnet(argc,argv)
int argc;
char *argv[];
{
void t_state(),rcv_char();
char *inet_ntoa(),*calloc();
int32 aton();
int send_tel();
int unix_send_tel();
struct session *s,*newsession();
struct telnet *tn;
struct tcb *tcb;
struct socket lsocket,fsocket;
lsocket.address = ip_addr;
lsocket.port = lport++;
fsocket.address = aton(argv[1]);
if(argc < 3)
fsocket.port = TELNET_PORT;
else
fsocket.port = atoi(argv[2]);
/* Allocate a session descriptor */
if((s = newsession()) == NULLSESSION){
printf("Too many sessions\r\n");
return 1;
}
s->type = TELNET;
if ((refuse_echo == 0) && (unix_line_mode != 0)) {
s->parse = unix_send_tel;
} else {
s->parse = send_tel;
}
current = s;
/* Create and initialize a Telnet protocol descriptor */
if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
printf(nospace);
s->type = FREE;
return 1;
}
tn->session = s; /* Upward pointer */
tn->state = TS_DATA;
s->cb.telnet = tn; /* Downward pointer */
tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,0,
rcv_char,NULLVFP,t_state,0,(int *)tn);
if(tcb == NULLTCB || tcb->state == CLOSED){
/* This is actually a bit dirty here. About the only time the
* state will be closed here is if we tried to connect to
* ourselves and got RST'ed. If this is true then the close
* upcall will already have freed the TCB and telnet block,
* so we're looking at the TCB after it's back on the heap.
*/
return 0;
}
tn->tcb = tcb; /* Downward pointer */
go();
return 0;
}
/* Process typed characters */
int
unix_send_tel(buf,n)
char *buf;
int16 n;
{
int i;
for (i=0; (i<n) && (buf[i] != '\r'); i++)
;
if (buf[i] == '\r') {
buf[i] = '\n';
n = i+1;
}
send_tel(buf,n);
}
int
send_tel(buf,n)
char *buf;
int16 n;
{
struct mbuf *bp,*qdata();
if(current == NULLSESSION || current->cb.telnet == NULLTN
|| current->cb.telnet->tcb == NULLTCB)
return;
bp = qdata(buf,n);
send_tcp(current->cb.telnet->tcb,bp);
}
/* Process incoming TELNET characters */
int
tel_input(tn,bp)
register struct telnet *tn;
struct mbuf *bp;
{
char c;
int ci;
void doopt(),dontopt(),willopt(),wontopt(),answer();
#ifdef FAST /* DON'T USE -- Aztec memchr() routine is broken */
char *memchr();
/* Optimization for very common special case -- no command chars */
if(tn->state == TS_DATA){
while(bp != NULLBUF && memchr(bp->data,IAC,bp->cnt) == NULLCHAR){
fflush(stdout);
write(1,bp->data,bp->cnt);
bp = free_mbuf(bp);
}
}
#endif
while(pullup(&bp,&c,1) == 1){
ci = c & 0xff;
switch(tn->state){
case TS_DATA:
if(ci == IAC){
tn->state = TS_IAC;
} else {
if(!tn->remote[TN_TRANSMIT_BINARY])
c &= 0x7f;
putchar(c);
}
break;
case TS_IAC:
switch(ci){
case WILL:
tn->state = TS_WILL;
break;
case WONT:
tn->state = TS_WONT;
break;
case DO:
tn->state = TS_DO;
break;
case DONT:
tn->state = TS_DONT;
break;
case IAC:
putchar(c);
tn->state = TS_DATA;
break;
default:
tn->state = TS_DATA;
break;
}
break;
case TS_WILL:
willopt(tn,ci);
tn->state = TS_DATA;
break;
case TS_WONT:
wontopt(tn,ci);
tn->state = TS_DATA;
break;
case TS_DO:
doopt(tn,ci);
tn->state = TS_DATA;
break;
case TS_DONT:
dontopt(tn,ci);
tn->state = TS_DATA;
break;
}
}
}
/* Telnet receiver upcall routine */
void
rcv_char(tcb,cnt)
register struct tcb *tcb;
int16 cnt;
{
struct mbuf *bp;
struct telnet *tn;
if((tn = (struct telnet *)tcb->user) == NULLTN){
/* Unknown connection; ignore it */
return;
}
/* Hold output if we're not the current session */
if(mode != CONV_MODE || current == NULLSESSION || current->cb.telnet != tn)
return;
if(recv_tcp(tcb,&bp,cnt) > 0)
tel_input(tn,bp);
fflush(stdout);
}
/* State change upcall routine */
void
t_state(tcb,old,new)
register struct tcb *tcb;
char old,new;
{
struct telnet *tn;
char notify = 0;
extern char *tcpstates[];
extern char *reasons[];
extern char *unreach[];
extern char *exceed[];
/* Can't add a check for unknown connection here, it would loop
* on a close upcall! We're just careful later on.
*/
tn = (struct telnet *)tcb->user;
if(current != NULLSESSION && current->type == TELNET && current->cb.telnet == tn)
notify = 1;
switch(new){
case CLOSE_WAIT:
if(notify)
printf("%s\r\n",tcpstates[new]);
close_tcp(tcb);
break;
case CLOSED: /* court adjourned */
if(notify){
printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
if(tcb->reason == NETWORK){
switch(tcb->type){
case DEST_UNREACH:
printf(": %s unreachable",unreach[tcb->code]);
break;
case TIME_EXCEED:
printf(": %s time exceeded",exceed[tcb->code]);
break;
}
}
printf(")\r\n");
cmdmode();
}
del_tcp(tcb);
if(tn != NULLTN)
free_telnet(tn);
break;
default:
if(notify)
printf("%s\r\n",tcpstates[new]);
break;
}
fflush(stdout);
}
/* Delete telnet structure */
static
free_telnet(tn)
struct telnet *tn;
{
if(tn->session != NULLSESSION)
freesession(tn->session);
if(tn != NULLTN)
free((char *)tn);
}
/* The guts of the actual Telnet protocol: negotiating options */
static
void
willopt(tn,opt)
struct telnet *tn;
int opt;
{
int ack;
void answer();
#ifdef DEBUG
printf("recv: will ");
if(opt <= NOPTIONS)
printf("%s\r\n",t_options[opt]);
else
printf("%u\r\n",opt);
#endif
switch(opt){
case TN_TRANSMIT_BINARY:
case TN_ECHO:
case TN_SUPPRESS_GA:
if(tn->remote[opt] == 1)
return; /* Already set, ignore to prevent loop */
if(opt == TN_ECHO){
if(refuse_echo){
/* User doesn't want to accept */
ack = DONT;
break;
} else
raw(); /* Put tty into raw mode */
}
tn->remote[opt] = 1;
ack = DO;
break;
default:
ack = DONT; /* We don't know what he's offering; refuse */
}
answer(tn,ack,opt);
}
static
void
wontopt(tn,opt)
struct telnet *tn;
int opt;
{
void answer();
#ifdef DEBUG
printf("recv: wont ");
if(opt <= NOPTIONS)
printf("%s\r\n",t_options[opt]);
else
printf("%u\r\n",opt);
#endif
if(opt <= NOPTIONS){
if(tn->remote[opt] == 0)
return; /* Already clear, ignore to prevent loop */
tn->remote[opt] = 0;
if(opt == TN_ECHO)
cooked(); /* Put tty into cooked mode */
}
answer(tn,DONT,opt); /* Must always accept */
}
static
void
doopt(tn,opt)
struct telnet *tn;
int opt;
{
void answer();
int ack;
#ifdef DEBUG
printf("recv: do ");
if(opt <= NOPTIONS)
printf("%s\r\n",t_options[opt]);
else
printf("%u\r\n",opt);
#endif
switch(opt){
#ifdef FUTURE /* Use when local options are implemented */
if(tn->local[opt] == 1)
return; /* Already set, ignore to prevent loop */
tn->local[opt] = 1;
ack = WILL;
break;
#endif
default:
ack = WONT; /* Don't know what it is */
}
answer(tn,ack,opt);
}
static
void
dontopt(tn,opt)
struct telnet *tn;
int opt;
{
void answer();
#ifdef DEBUG
printf("recv: dont ");
if(opt <= NOPTIONS)
printf("%s\r\n",t_options[opt]);
else
printf("%u\r\n",opt);
#endif
if(opt <= NOPTIONS){
if(tn->local[opt] == 0){
/* Already clear, ignore to prevent loop */
return;
}
tn->local[opt] = 0;
}
answer(tn,WONT,opt);
}
static
void
answer(tn,r1,r2)
struct telnet *tn;
int r1,r2;
{
struct mbuf *bp,*qdata();
char s[3];
#ifdef DEBUG
switch(r1){
case WILL:
printf("sent: will ");
break;
case WONT:
printf("sent: wont ");
break;
case DO:
printf("sent: do ");
break;
case DONT:
printf("sent: dont ");
break;
}
if(r2 <= 6)
printf("%s\r\n",t_options[r2]);
else
printf("%u\r\n",r2);
#endif
s[0] = IAC;
s[1] = r1;
s[2] = r2;
bp = qdata(s,(int16)3);
send_tcp(tn->tcb,bp);
}
SHAR_EOF
# End of shell archive
exit 0
--
Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page
Have five nice days.